package org.amos.tenant.core;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.amos.core.basic.constant.SystemConstant;
import org.amos.core.basic.context.TenantContextHolder;
import org.amos.core.basic.exception.TenantException;
import org.amos.core.basic.utils.HttpUtils;
import org.amos.core.basic.vo.R;
import org.amos.tenant.properties.TenantProperties;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Objects;

/**
 * 多租户 过滤器
 * 1. 如果是登陆的用户，校验是否有权限访问该租户，避免越权问题。
 * 2. 如果请求未带租户的编号，检查是否是忽略的 URL，否则也不允许访问。
 * 3. 校验租户是合法，例如说被禁用、到期
 */
@Slf4j
public class TenantFilter extends OncePerRequestFilter {

    private TenantProperties tenantProperties;

    private TenantApi tenantApi;

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    public TenantFilter(TenantProperties tenantProperties, TenantApi tenantApi) {
        this.tenantProperties = tenantProperties;
        this.tenantApi = tenantApi;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        // 对外以租户编号形式传输，避免ID暴露
        String tenantNo = request.getHeader(SystemConstant.SYS_TENANT_NO);
        Long tenantId = null;
        if (StrUtil.isNotBlank(tenantNo)) {
            tenantId = tenantApi.getTenantId(tenantNo);
            Assert.state(Objects.nonNull(tenantId), "tenantId is null");
            TenantContextHolder.setTenantId(tenantId);
        }

        try {
            if (!isIgnoreUrl(request)) {
                if (Objects.isNull(tenantId)) {
                    log.error("[TenantSecurityWebFilter]request uri:{} 租户ID为空]", request.getRequestURI());
                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    HttpUtils.write(response, R.error("缺少必要参数"));
                    return;
                }
                try {
                    //校验租户是合法
                    tenantApi.valid(tenantId);
                } catch (TenantException e) {
                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    HttpUtils.write(response, R.error(e.getMessage()));
                    return;
                }
            } else {
                TenantContextHolder.setIgnore(true);
            }
            chain.doFilter(request, response);
        } finally {
            TenantContextHolder.clear();
        }
    }

    private boolean isIgnoreUrl(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        // 检查请求uri是否在白名单内
        for (String url : tenantProperties.getExcludeUrls()) {
            if (pathMatcher.match(url, request.getRequestURI())) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        boolean flag = false;
        if (!tenantProperties.getEnabled()) {
            flag = true;
        }
        return flag;
    }
}
